﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Engine.Dtd.Schema {
    /// <summary>
    /// Represents the content model of SGML element that specifies what element occurrences may legitimately contain.
    /// </summary>
    public sealed class DContentModel {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal static DName[] DefaultExclusions = new DName[0];
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private DModelGroups groups;
        
        internal DContentModel(DModelGroups modelGroups) {
            Guard.ArgumentNotNull(modelGroups, "modelGroups");
            this.groups = modelGroups;
            this.Inclusions = new InclusionCollection(this);
            this.Exclusions = DefaultExclusions;
            }

        /// <summary>
        /// Gets the model groups.
        /// </summary>
        public DModelGroups Groups { get { return groups; } }
        /// <summary>
        /// Gets the content model type.
        /// </summary>
        public DContentModelType ContentType { get; internal set; }
        /// <summary>
        /// Gets a collection of additional elements that can be included at any point in this model
        /// or any of its constituent elements.
        /// </summary>
        public ICollection<DName> Inclusions { get; internal set; }
        /// <summary>
        /// Gets a collection of elements that cannot be included within this model.
        /// </summary>
        public IEnumerable<DName> Exclusions { get; internal set; }

        /// <summary>
        /// Gets a value indicating whether this group contains only text.
        /// </summary>
        /// <value>true if the group is of mixed content and has no model groups, otherwise false.</value>
        public bool TextOnly {
            get { return groups.Count == 1 && groups[0].Name == DName.PCData; }
            }

        /// <summary>
        /// Gets a value indicating whether this group contains text and components.
        /// </summary>
        public bool Mixed {
            get { return groups.Count > 1 && false; }// groups.Any(e => e.Name == DName.PCData); }
            }

        internal bool Exists(DName name) {
            if (ContentType == DContentModelType.Empty)
                return false;
            foreach (DName item in Exclusions)
                if (name == item)
                    return false;
            foreach (DName item in Inclusions)
                if (name == item)
                    return true;
            return Groups.Contains(name);
            }

        internal void InitializeInclusionExceptions() {
            if (ContentType == DContentModelType.Default && Inclusions.Count > 0) {
                foreach (IModelGroupsNode item in Groups) {
                    if (item.Declaration != null) {
                        DContentModel model = item.Declaration.ContentModel;
                        if (model.Inclusions.Count > 0) {
                            IEnumerable<DName> difference = Inclusions.Except(model.Inclusions);
                            if (difference.Count() > 0)
                                model.Inclusions = new InclusionCollection(model, model.Inclusions.Concat(difference));
                            else
                                continue;
                            }
                        else if (model.HasExclusionExceptions()) {
                            IEnumerable<DName> difference = Inclusions.Except(model.Exclusions);
                            if (difference.Count() > 0)
                                model.Inclusions = new InclusionCollection(model, difference);
                            }
                        else
                            model.Inclusions = new InclusionCollection(model, Inclusions);
                        model.InitializeInclusionExceptions();
                        }
                    }
                }
            }
        }
    }
